export default (config) => {
    let cache = {};
    let cache_time = {};
    let type = config.cache.type;
    let client = null;

    switch (type) {
        case 'redis':
            let redis = require('redis');
            client = redis.createClient(config.redis);
            break;
    }

    let Cache = {
        set(key,data,time) {
            if (time && !cache_time[key]) {
                cache_time[key] = Date.now()+time;
            }
            switch (type) {
                case 'memory':
                    cache[key] = new Buffer(JSON.stringify(data));
                    break;
                case 'redis':
                    client.set(key, new Buffer(JSON.stringify(data)));
            }
        },

        async get(key) {
            if (cache_time[key] && cache_time[key] < Date.now()) {
                delete cache[key];
                cache_time[key] = null;
            }

            switch (type) {
                case 'memory':
                    return cache[key]?JSON.parse(cache[key]):null;
                case 'redis':
                    return await (new Promise(function (resolver, reject) {
                        client.get(key, (err, data) => {
                            if (err) reject(err);
                            else resolver(data?JSON.parse(data):null);
                        })
                    }));
            }
        },

        sequelize() {
            let database = config.database;
            
            database[3].define.classMethods = database[3].define.classMethods||{
                cache(time) {
                    this.iscache = true;
                    this.cache_time = time;
                    return this;
                },
                async findAll(opts) {return await sequelize_cache.apply(this,['findAll',opts]) },
                async findById(param,opts) {return await sequelize_cache.apply(this,['findById',opts,param]) },
                async findOne(opts) {return await sequelize_cache.apply(this,['findOne',opts]) },
            }

            database[3].define.hooks = database[3].define.hooks||{
                afterFind: sequelize_set_cache,
                afterFindById: sequelize_set_cache,
                afterFindOne: sequelize_set_cache,
            }
            return database;
        },
    }

    async function sequelize_cache(action,opts,param) {
        opts = opts || {};

        if (this.iscache) {
            let cache_key = JSON.stringify({
                table:this.getTableName(opts),
                attributes:Object.keys(this.tableAttributes),
                options: opts
            });
            let data = await Cache.get(cache_key);

            if (data) return data;

            opts.cache_key = cache_key;
            opts.cache_time = this.cache_time;
            this.iscache = false;
            this.cache_time = false;
        }

        let params = [];
        if (param) {params.push(param)};
        params.push(opts);
        return this.__proto__[action].apply(this,params);
    }

    function sequelize_set_cache(res, opts) {
        if (opts.cache_key) {
            Cache.set(opts.cache_key, res, opts.cache_time);
        }
    }

    return Cache
};
